Avaa Pythonin 'email'-paketti. Opi rakentamaan monimutkaisia MIME-viestejä ja jäsentämään saapuvia sähköposteja tehokkaasti ja globaalisti.
Pythonin sähköpostipaketin hallinta: MIME-viestien rakentamisen ja jäsennöinnin taide
Sähköposti on edelleen globaalin viestinnän kulmakivi, välttämätön henkilökohtaisessa kirjeenvaihdossa, liiketoiminnassa ja automaattisissa järjestelmäilmoituksissa. Jokaisen rikastetun tekstisähköpostin, liitteen ja huolellisesti muotoillun allekirjoituksen takana on Multipurpose Internet Mail Extensions (MIME) -laajennuksen monimutkaisuus. Kehittäjille, erityisesti Pythonilla työskenteleville, näiden MIME-viestien ohjelmallinen rakentaminen ja jäsennöinti on kriittinen taito.
Pythonin sisäänrakennettu email
-paketti tarjoaa vankan ja kattavan kehyksen sähköpostiviestien käsittelyyn. Se ei ole vain yksinkertaisten tekstien lähettämiseen; se on suunniteltu abstrahoimaan MIME:n monimutkaiset yksityiskohdat, antaen sinulle mahdollisuuden luoda hienostuneita sähköposteja ja poimia tiettyjä tietoja saapuvista sähköposteista merkittävällä tarkkuudella. Tämä opas vie sinut syvälle paketin kahteen pääasialliseen osa-alueeseen: MIME-viestien rakentamiseen lähetystä varten ja niiden jäsennöintiin tiedonkeruuta varten, tarjoten globaalin näkökulman parhaisiin käytäntöihin.
Sekä rakentamisen että jäsennöinnin ymmärtäminen on ratkaisevan tärkeää. Kun rakennat viestiä, määrittelet sen rakenteen ja sisällön toiselle järjestelmälle tulkittavaksi. Kun jäsennetään, tulkitaan toisen järjestelmän määrittelemää rakennetta ja sisältöä. Syvä ymmärrys toisesta auttaa suuresti toisen hallinnassa, johtaen vankempiin ja yhteentoimiviin sähköpostisovelluksiin.
MIME:n ymmärtäminen: Nykyaikaisen sähköpostin selkäranka
Ennen Python-erityispiirteisiin sukeltamista on tärkeää ymmärtää, mitä MIME on ja miksi se on niin tärkeä. Alun perin sähköpostiviestit rajoittuivat pelkkään tekstiin (7-bittiset ASCII-merkit). MIME, joka otettiin käyttöön 1990-luvun alussa, laajensi sähköpostin ominaisuuksia tukemaan:
- Ei-ASCII-merkkejä: Sallii tekstin kielillä, kuten arabia, kiina, venäjä tai millä tahansa muulla kielellä, joka käyttää merkkejä ASCII-joukon ulkopuolella.
- Liitteet: Tiedostojen, kuten asiakirjojen, kuvien, äänen ja videon lähettäminen.
- Rikastetun tekstin muotoilu: HTML-sähköpostit, joissa on lihavointia, kursivointia, värejä ja asetteluja.
- Useita osia: Yhdistämällä pelkkä teksti, HTML ja liitteet yhden sähköpostin sisälle.
MIME saavuttaa tämän lisäämällä erityisiä otsikoita sähköpostiviestiin ja jäsentämällä sen rungon erilaisiin "osiin". Tärkeimpiä kohtaamiasi MIME-otsikoita ovat:
Content-Type:
Määrittää osan datan tyypin (esim.text/plain
,text/html
,image/jpeg
,application/pdf
,multipart/alternative
). Se sisältää usein myöscharset
-parametrin (esim.charset=utf-8
).Content-Transfer-Encoding:
Osoittaa, miten sähköpostiohjelman tulisi dekoodata sisältö (esim.base64
binääridatalle,quoted-printable
pääasiassa tekstille, jossa on joitain ei-ASCII-merkkejä).Content-Disposition:
Ehdottaa, miten vastaanottajan sähköpostiohjelman tulisi näyttää osa (esim.inline
näytettäväksi viestin rungossa,attachment
tallennettavaksi tiedostoksi).
Pythonin email
-paketti: Syväsukellus
Pythonin email
-paketti on kattava kirjasto, joka on suunniteltu sähköpostiviestien ohjelmalliseen luomiseen, jäsennöintiin ja muokkaamiseen. Se on rakennettu Message
-objektien ympärille, jotka edustavat sähköpostin rakennetta.
Keskeisiä moduuleja paketissa ovat:
email.message:
Sisältää ydinluokanEmailMessage
, joka on ensisijainen rajapinta sähköpostiviestien luomiseen ja manipulointiin. Se on erittäin joustava luokka, joka käsittelee MIME-yksityiskohdat automaattisesti.email.mime:
Tarjoaa vanhempia luokkia (kutenMIMEText
,MIMEMultipart
), jotka tarjoavat tarkemman hallinnan MIME-rakenteeseen. VaikkaEmailMessage
on yleensä suositeltava uuteen koodiin sen yksinkertaisuuden vuoksi, näiden luokkien ymmärtäminen voi olla hyödyllistä.email.parser:
Tarjoaa luokkia, kutenBytesParser
jaParser
, raa'an sähköpostidatan (tavut tai merkkijonot) muuntamiseksiEmailMessage
-objekteiksi.email.policy:
Määrittää käytäntöjä, jotka ohjaavat sähköpostiviestien rakentamista ja jäsennöintiä, vaikuttaen otsikoiden koodaukseen, rivinvaihtoihin ja virheiden käsittelyyn.
Useimmissa nykyaikaisissa käyttötapauksissa käytät ensisijaisesti email.message.EmailMessage
-luokkaa sekä rakentamisessa että jäsennellyssä viestiobjektissa. Sen metodit yksinkertaistavat huomattavasti sitä, mikä aiemmin oli monimutkaisempaa vanhemmilla email.mime
-luokilla.
MIME-viestien rakentaminen: Sähköpostien luominen tarkasti
Sähköpostien rakentaminen sisältää erilaisten komponenttien (teksti, HTML, liitteet) kokoamisen kelvolliseksi MIME-rakenteeksi. EmailMessage
-luokka yksinkertaistaa tätä prosessia merkittävästi.
Perus tekstisähköpostit
Yksinkertaisin sähköposti on pelkkä teksti. Voit luoda sellaisen ja asettaa perusotsikot vaivattomasti:
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Terveisiä Pythonista'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Hei, tämä on tavallinen tekstisähköposti lähetetty Pythonista.\n\nParhain terveisin,\nSinun Python-skriptisi')
print(msg.as_string())
Selitys:
EmailMessage()
luo tyhjän viestiobjektin.- Sanaluettelon kaltainen käyttö (
msg['Subject'] = ...
) asettaa yleisiä otsikoita. set_content()
lisää sähköpostin pääsisällön. Oletuksena se päätteleeContent-Type: text/plain; charset="utf-8"
.as_string()
sarjoittaa viestin merkkijonomuotoon, joka sopii SMTP:llä lähettämiseen tai tiedostoon tallentamiseen.
HTML-sisällön lisääminen
HTML-sähköpostin lähettämiseksi määrität vain sisältötyypin kutsuttaessa set_content()
. On hyvä käytäntö tarjota pelkkä tekstivaihtoehto vastaanottajille, joiden sähköpostiohjelmat eivät renderöi HTML:ää, tai saavutettavuussyistä.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Sinun HTML-uutiskirjeesi'
msg['From'] = 'newsletter@example.com'
msg['To'] = 'subscriber@example.com'
html_content = """
<html>
<head></head>
<body>
<h1>Tervetuloa globaaliin päivitykseemme!</h1>
<p>Hyvä tilaaja,</p>
<p>Tämä on <strong>viimeisin päivityksesi</strong> ympäri maailmaa.</p>
<p>Vieraile <a href="http://www.example.com">verkkosivustollamme</a> saadaksesi lisätietoja.</p>
<p>Parhain terveisin,<br>Tiimi</p>
</body>
</html>
"""
# Lisää HTML-versio
msg.add_alternative(html_content, subtype='html')
# Lisää pelkkä tekstivaihtoehto
plain_text_content = (
"Tervetuloa globaaliin päivitykseemme!\n\n"
"Hyvä tilaaja,\n\n"
"Tämä on viimeisin päivityksesi ympäri maailmaa.\n"
"Vieraile verkkosivustollamme saadaksesi lisätietoja: http://www.example.com\n\n"
"Parhain terveisin,\nTiimi"
)
msg.add_alternative(plain_text_content, subtype='plain')
print(msg.as_string())
Selitys:
add_alternative()
-metodia käytetään lisäämään *saman* sisällön eri esitysmuotoja. Sähköpostiohjelma näyttää parhaan käsittelemänsä version (yleensä HTML).- Tämä luo automaattisesti
multipart/alternative
MIME-rakenteen.
Liitteiden käsittely
Tiedostojen liittäminen on suoraviivaista add_attachment()
-metodilla. Voit liittää minkä tahansa tyyppisen tiedoston, ja paketti käsittelee asianmukaiset MIME-tyypit ja koodaukset (yleensä base64
).
from email.message import EmailMessage
from pathlib import Path
# Luodaan demonstrointia varten väliaikaisia tiedostoja
Path('report.pdf').write_bytes(b'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n2 0 obj<</Count 0>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\ntrailer<</Size 3/Root 1 0 R>>startxref\n104\n%%EOF') # Hyvin perustason, virheellinen PDF-paikkamerkki
Path('logo.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82') # 1x1 läpinäkyvä PNG-paikkamerkki
msg = EmailMessage()
msg['Subject'] = 'Tärkeä asiakirja ja kuva'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Löydät liitteenä olevan raportin ja yrityksen logon.')
# Liitä PDF-tiedosto
with open('report.pdf', 'rb') as f:
file_data = f.read()
msg.add_attachment(
file_data,
maintype='application',
subtype='pdf',
filename='Annual_Report_2024.pdf'
)
# Liitä kuvatiedosto
with open('logo.png', 'rb') as f:
image_data = f.read()
msg.add_attachment(
image_data,
maintype='image',
subtype='png',
filename='CompanyLogo.png'
)
print(msg.as_string())
# Poistetaan väliaikaiset tiedostot
Path('report.pdf').unlink()
Path('logo.png').unlink()
Selitys:
add_attachment()
ottaa vastaan tiedoston sisällön raakoina tavuina.maintype
jasubtype
määrittävät MIME-tyypin (esim.application/pdf
,image/png
). Nämä ovat ratkaisevia vastaanottajan sähköpostiohjelmalle, jotta liite tunnistetaan ja käsitellään oikein.filename
tarjoaa nimen, jolla liite tallennetaan vastaanottajalle.- Tämä luo automaattisesti
multipart/mixed
-rakenteen.
Moniosaisten viestien luominen
Kun viestissä on sekä HTML-runko, pelkkä tekstivaihtoehto ja inline-kuvia tai muita liittyviä tiedostoja, tarvitset monimutkaisemman moniosaisen rakenteen. EmailMessage
-luokka käsittelee tämän älykkäästi add_related()
ja add_alternative()
-metodeilla.
Yleinen käyttötapaus on HTML-sähköposti, jossa kuva on upotettu suoraan HTML:ään ("inline"-kuva). Tämä käyttää multipart/related
-tyyppiä.
from email.message import EmailMessage
from pathlib import Path
# Luodaan väliaikainen kuvatiedosto demonstrointia varten (1x1 läpinäkyvä PNG)
Path('banner.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82')
msg = EmailMessage()
msg['Subject'] = 'Inline-kuvaesimerkki'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# Pelkkä tekstiversio (varavaihtoehto)
plain_text = 'Tarkista upea bannerimme!\n\n[Kuva: Banner.png]\n\nVieraile sivustollamme.'
msg.set_content(plain_text, subtype='plain') # Aseta alkuperäinen pelkkä tekstisisältö
# HTML-versio (CID:llä inline-kuvalle)
html_content = """
<html>
<head></head>
<body>
<h1>Viimeisin tarjouksemme!</h1>
<p>Hyvä asiakas,</p>
<p>Älä missaa erikoistarjousta maailmanlaajuisesti:</p>
<img src="cid:my-banner-image" alt="Tarjousbanneri">
<p>Klikkaa <a href="http://www.example.com">tätä</a> saadaksesi lisätietoja.</p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype='html') # Lisää HTML-vaihtoehto
# Lisää inline-kuva (liittyvä sisältö)
with open('banner.png', 'rb') as img_file:
image_data = img_file.read()
msg.add_related(
image_data,
maintype='image',
subtype='png',
cid='my-banner-image' # Tämä CID vastaa HTML:n "src"-attribuuttia
)
print(msg.as_string())
# Poistetaan väliaikainen tiedosto
Path('banner.png').unlink()
Selitys:
set_content()
luo alkuperäisen sisällön (tässä pelkkä teksti).add_alternative()
lisää HTML-version, luodenmultipart/alternative
-rakenteen, joka sisältää pelkän tekstin ja HTML-osat.add_related()
-metodia käytetään sisällölle, joka on "liittyvä" johonkin viestin osaan, tyypillisesti inline-kuviin HTML:ssä. Se ottaacid
(Content-ID) -parametrin, johon sitten viitataan HTML:n<img src="cid:my-banner-image">
-tagissa.- Lopullinen rakenne on
multipart/mixed
(jos olisi ollut ulkoisia liitteitä), joka sisältäämultipart/alternative
-osan, joka puolestaan sisältäämultipart/related
-osan.multipart/related
-osa sisältää HTML:n ja inline-kuvan.EmailMessage
-luokka hoitaa tämän sisäkkäisyyden monimutkaisuuden puolestasi.
Koodaukset ja merkistöt globaalia käyttöä varten
Kansainvälisessä viestinnässä asianmukainen merkistökoodaus on ensiarvoisen tärkeää. email
-paketti käyttää oletusarvoisesti UTF-8:aa, joka on yleismaailmallinen standardi erilaisten merkistöjen käsittelyyn ympäri maailmaa.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Globaalit merkit: こんにちは, Привет, नमस्ते'
msg['From'] = 'global_sender@example.com'
msg['To'] = 'global_recipient@example.com'
# Japanilaiset, venäläiset ja hindinkieliset merkit
content = "Tämä viesti sisältää erilaisia globaaleja merkkejä:\n"
content += "こんにちは (japani)\n"
content += "Привет (venäjä)\n"
content += "नमस्ते (hindi)\n\n"
content += "'email'-paketti käsittelee UTF-8:n sulavasti."
msg.set_content(content)
print(msg.as_string())
Selitys:
- Kun
set_content()
vastaanottaa Python-merkkijonon, se koodaa sen automaattisesti UTF-8:ksi ja asettaa otsikonContent-Type: text/plain; charset="utf-8"
. - Jos sisältö vaatii sitä (esim. sisältää paljon ei-ASCII-merkkejä), se voi myös soveltaa
Content-Transfer-Encoding: quoted-printable
taibase64
varmistaakseen turvallisen siirron vanhemmissa sähköpostijärjestelmissä. Paketti hoitaa tämän automaattisesti valitun käytännön mukaan.
Mukautetut otsikot ja käytännöt
Voit lisätä minkä tahansa mukautetun otsikon sähköpostiin. Käytännöt (email.policy
-moduulista) määrittelevät, miten viestejä käsitellään, ja vaikuttavat esimerkiksi otsikoiden koodaukseen, rivinvaihtoihin ja virheiden käsittelyyn. Oletuskäytäntö on yleensä hyvä, mutta voit valita `SMTP`:n tiukkaan SMTP-yhteensopivuuteen tai määritellä omia käytäntöjä.
from email.message import EmailMessage
from email import policy
msg = EmailMessage(policy=policy.SMTP)
msg['Subject'] = 'Sähköposti mukautetulla otsikolla'
msg['From'] = 'info@example.org'
msg['To'] = 'user@example.org'
msg['X-Custom-Header'] = 'Tämä on mukautettu arvo seurantaa varten'
msg['Reply-To'] = 'support@example.org'
msg.set_content('Tämä sähköposti demonstroi mukautettuja otsikoita ja käytäntöjä.')
print(msg.as_string())
Selitys:
policy=policy.SMTP
-käytön varmistaa tiukan SMTP-standardien noudattamisen, mikä voi olla ratkaisevan tärkeää toimitettavuuden kannalta.- Mukautetut otsikot lisätään samalla tavalla kuin tavallisetkin. Ne alkavat usein
X-
-etuliitteellä merkitystenä epästandardeista otsikoista.
MIME-viestien jäsennöinti: Tiedon kerääminen saapuvista sähköposteista
Jäsennöinti tarkoittaa raa'an sähköpostidatan (tyypillisesti IMAP:n tai tiedoston kautta vastaanotetun) muuntamista EmailMessage
-objektiksi, jota voit sitten helposti tarkastella ja manipuloida.
Lataaminen ja alkujäsennys
Saat sähköposteja yleensä raakoina tavuina. Tätä varten käytetään email.parser.BytesParser
-luokkaa (tai käteviä funktioita email.message_from_bytes()
).
from email.parser import BytesParser
from email.policy import default
raw_email_bytes = b"""
From: sender@example.com
To: recipient@example.com
Subject: Test Email with Basic Headers
Date: Mon, 1 Jan 2024 10:00:00 +0000
Content-Type: text/plain; charset=\"utf-8\"
This is the body of the email.
It's a simple test.
"""
# Käyttäen BytesParseria
parser = BytesParser(policy=default)
msg = parser.parsebytes(raw_email_bytes)
# Tai käyttämällä kätevyysfunktiota
# from email import message_from_bytes
# msg = message_from_bytes(raw_email_bytes, policy=default)
print(f"Subject: {msg['subject']}")
print(f"From: {msg['from']}")
print(f"Content-Type: {msg['Content-Type']}")
Selitys:
BytesParser
ottaa vastaan raakaa tavudataa (jona sähköpostit siirretään) ja palauttaaEmailMessage
-objektin.policy=default
määrittää jäsennyssäännöt.
Otsikoiden käyttäminen
Otsikot ovat helposti saatavilla sanaluettelon kaltaisilla avaimilla. Paketti hoitaa automaattisesti koodattujen otsikoiden (esim. kansainvälisiä merkkejä sisältävät aiheet) dekoodauksen.
# ... (käyttäen edellisen jäsennöintiesimerkin 'msg'-objektia)
print(f"Date: {msg['date']}")
print(f"Message ID: {msg['Message-ID'] if 'Message-ID' in msg else 'N/A'}")
# Useiden otsikoiden käsittely (esim. 'Received'-otsikot)
# from email.message import EmailMessage # Jos ei vielä tuotu
# from email import message_from_string # Nopeaan merkkijonoesimerkkiin
multi_header_email = message_from_string(
"""
From: a@example.com
To: b@example.com
Subject: Multi-header Test
Received: from client.example.com (client.example.com [192.168.1.100])
by server.example.com (Postfix) with ESMTP id 123456789
for <b@example.com>; Mon, 1 Jan 2024 10:00:00 +0000 (GMT)
Received: from mx.another.com (mx.another.com [192.168.1.101])
by server.example.com (Postfix) with ESMTP id 987654321
for <b@example.com>; Mon, 1 Jan 2024 09:59:00 +0000 (GMT)
Body content here.
"""
)
received_headers = multi_header_email.get_all('received')
if received_headers:
print("\nReceived Headers:")
for header in received_headers:
print(f"- {header}")
Selitys:
- Otsikon käyttäminen palauttaa sen arvon merkkijonona.
get_all('otsikon-nimi')
on hyödyllinen otsikoille, jotka voivat esiintyä useita kertoja (kutenReceived
).- Paketti hoitaa otsikoiden dekoodauksen, joten esimerkiksi
Subject: =?utf-8?Q?Global_Characters:_=E3=81=93=E3=82=93=E3=81=AB=E3=81=A1=E3=81=AF?=
muuttuu automaattisesti luettavaksi merkkijonoksi.
Rungon sisällön kerääminen
Varsinaisen viestin rungon kerääminen vaatii sen tarkistamista, onko viesti moniosainen. Moniosaisille viesteille iteroidaan sen osia.
from email.message import EmailMessage
from email import message_from_string
multipart_email_raw = """
From: multi@example.com
To: user@example.com
Subject: Test Multipart Email
Content-Type: multipart/alternative; boundary="_----------=_12345"
--_----------=_12345
Content-Type: text/plain; charset="utf-8"
Hello from the plain text part!
--_----------=_12345
Content-Type: text/html; charset="utf-8"
<html>
<body>
<h1>Hello from the HTML part!</h1>
<p>This is a <strong>rich text</strong> email.</p>
</body>
</html>
--_----------=_12345--
"""
msg = message_from_string(multipart_email_raw)
if msg.is_multipart():
print("\n--- Multipart Email Body ---")
for part in msg.iter_parts():
content_type = part.get_content_type()
charset = part.get_content_charset() or 'utf-8' # Oletetaan utf-8, jos ei määritelty
payload = part.get_payload(decode=True) # Dekoodaa payload-tavut
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {content_type}, Charset: {charset}\nContent:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content-Type: {content_type}, Charset: {charset}\nContent: (Binary or undecodable data)\n")
# Käsittele binääritiedot tai yritä varakoodausta
else:
print("\n--- Single Part Email Body ---")
charset = msg.get_content_charset() or 'utf-8'
payload = msg.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {msg.get_content_type()}, Charset: {charset}\nContent:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content: (Binary or undecodable data)\n")
Selitys:
is_multipart()
määrittää, onko sähköpostissa useita osia.iter_parts()
käy läpi kaikki moniosaisen viestin osat.get_content_type()
palauttaa täydellisen MIME-tyypin (esim.text/plain
).get_content_charset()
kerää merkkijononContent-Type
-otsikosta.get_payload(decode=True)
on ratkaiseva: se palauttaa *dekoodatun* sisällön tavuina. Sinun on sitten.decode()
nämä tavut käyttämällä oikeaa merkkijonoa saadaksesi Python-merkkijonon.
Liitteiden käsittely jäsennöinnin aikana
Liitteet ovat myös moniosaisen viestin osia. Voit tunnistaa ne Content-Disposition
-otsikon avulla ja tallentaa niiden dekoodatun sisällön.
from email.message import EmailMessage
from email import message_from_string
import os
# Esimerkkisähköposti, jossa on yksinkertainen liite
email_with_attachment = """
From: attach@example.com
To: user@example.com
Subject: Document Attached
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_----------=_XYZ"
--_----------=_XYZ
Content-Type: text/plain; charset="utf-8"
Here is your requested document.
--_----------=_XYZ
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="document.pdf"
JVBERi0xLjQKMSAwIG9iagpbL1BERi9UZXh0L0ltYWdlQy9JbWFnZUkvSW1hZ0VCXQplbmRvYmoK
--_----------=_XYZ--
"""
msg = message_from_string(email_with_attachment)
output_dir = 'parsed_attachments'
os.makedirs(output_dir, exist_ok=True)
print("\n--- Processing Attachments ---")
for part in msg.iter_attachments():
filename = part.get_filename()
if filename:
filepath = os.path.join(output_dir, filename)
try:
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f"Saved attachment: {filepath} (Type: {part.get_content_type()})")
except Exception as e:
print(f"Error saving {filename}: {e}")
else:
print(f"Found an attachment without a filename (Content-Type: {part.get_content_type()})")
# Poistetaan tulostuskansio
# import shutil
# shutil.rmtree(output_dir)
Selitys:
iter_attachments()
palauttaa erikseen osat, jotka todennäköisesti ovat liitteitä (eli joilla onContent-Disposition: attachment
-otsikko tai jotka eivät ole muuten luokiteltu).get_filename()
kerää tiedostonimenContent-Disposition
-otsikosta.part.get_payload(decode=True)
hakee liitteen raa'an binäärisisällön, joka on jo dekoodattubase64
- taiquoted-printable
-muodosta.
Koodausten ja merkistöjen dekoodaus
email
-paketti tekee erinomaista työtä dekoodaamalla automaattisesti yleiset siirtokoodaukset (kuten base64
, quoted-printable
), kun kutsut get_payload(decode=True)
. Tekstisisällölle se yrittää käyttää Content-Type
-otsikossa määriteltyä charset
-arvoa. Jos merkistöä ei ole määritelty tai se on virheellinen, saatat joutua käsittelemään sen asianmukaisesti.
from email.message import EmailMessage
from email import message_from_string
# Esimerkki mahdollisesti ongelmallisella merkistöllä
email_latin1 = """
From: legacy@example.com
To: new_system@example.com
Subject: Special characters: àéíóú
Content-Type: text/plain; charset="iso-8859-1"
This message contains Latin-1 characters: àéíóú
"""
msg = message_from_string(email_latin1)
if msg.is_multipart():
for part in msg.iter_parts():
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
try:
print(f"Decoded (Charset: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Failed to decode with {charset}. Trying fallback...")
# Varakoodaus yleiseen merkistöön tai 'latin-1':een, jos sitä odotetaan
print(f"Decoded (Fallback Latin-1): {payload.decode('latin-1', errors='replace')}")
else:
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or 'utf-8'
try:
print(f"Decoded (Charset: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Failed to decode with {charset}. Trying fallback...")
print(f"Decoded (Fallback Latin-1): {payload.decode('latin-1', errors='replace')}")
Selitys:
- Yritä aina käyttää
Content-Type
-otsikossa määriteltyä merkistöä. - Käytä
try-except UnicodeDecodeError
-lohkoa kestävyyden vuoksi, erityisesti käsitellessäsi sähköposteja erilaisista ja mahdollisesti epästandardeista lähteistä. errors='replace'
taierrors='ignore'
voidaan käyttää.decode()
-metodin kanssa merkkien käsittelyyn, joita ei voida mapata kohdekoodaukseen, estäen kaatumiset.
Edistyneet jäsennysskenaariot
Todelliset sähköpostit voivat olla erittäin monimutkaisia, sisäkkäisillä moniosaisilla rakenteilla. email
-paketin rekursiivinen luonne tekee näiden navigoinnista suoraviivaista. Voit yhdistää is_multipart()
ja iter_parts()
syvälle sisäkkäisten viestien läpikäymiseksi.
from email.message import EmailMessage
from email import message_from_string
def parse_email_part(part, indent=0):
prefix = " " * indent
content_type = part.get_content_type()
charset = part.get_content_charset() or 'N/A'
print(f"{prefix}Part Type: {content_type}, Charset: {charset}")
if part.is_multipart():
for subpart in part.iter_parts():
parse_email_part(subpart, indent + 1)
elif part.get_filename(): # Se on liite
print(f"{prefix} Attachment: {part.get_filename()} (Size: {len(part.get_payload(decode=True))}")
else: # Se on tavallinen teksti/html-runko-osa
payload = part.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
# print(f"{prefix} Content (first 100 chars): {decoded_content[:100]}...") # Lyhyyden vuoksi
except UnicodeDecodeError:
print(f"{prefix} Content: (Binary or undecodable text)")
complex_email_raw = """
From: complex@example.com
To: receiver@example.com
Subject: Complex Email with HTML, Plain, and Attachment
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="outer_boundary"
--outer_boundary
Content-Type: multipart/alternative; boundary="inner_boundary"
--inner_boundary
Content-Type: text/plain; charset="utf-8"
Plain text content.
--inner_boundary
Content-Type: text/html; charset="utf-8"
<html><body><h2>HTML Content</h2></body></html>
--inner_boundary--
--outer_boundary
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="data.bin"
SGVsbG8gV29ybGQh
--outer_boundary--
"""
msg = message_from_string(complex_email_raw)
print("\n--- Traversing Complex Email Structure ---")
parse_email_part(msg)
Selitys:
- Rekursiivinen funktio
parse_email_part
demonstroi, miten koko viestipuuta käydään läpi, tunnistetaan moniosaiset osat, liitteet ja runkon sisältö jokaisella tasolla. - Tämä malli on erittäin joustava syvälle sisäkkäisten sähköpostien tiettyjen sisältötyyppien keräämiseen.
Rakentaminen vs. Jäsennöinti: Vertailunäkökulma
Vaikka ne ovat erillisiä toimintoja, rakentaminen ja jäsennöinti ovat saman kolikon kaksi puolta: MIME-viestien käsittely. Yhden ymmärtäminen auttaa väistämättä toista.
Rakentaminen (Lähetys):
- Pääpaino: Otsikoiden, sisällön ja liitteiden oikea kokoaminen standardien mukaiselle MIME-rakenteelle.
- Ensisijainen työkalu:
email.message.EmailMessage
-luokka metodeilla, kutenset_content()
,add_attachment()
,add_alternative()
,add_related()
. - Keskeiset haasteet: Oikeiden MIME-tyyppien, merkistöjen (erityisesti UTF-8 globaalia tukea varten) ja
Content-Transfer-Encoding
-otsikoiden varmistaminen sekä asianmukainen otsikoiden muotoilu. Virheet voivat johtaa siihen, että sähköposteja ei näytetä oikein, liitteet ovat vioittuneita tai viestit merkitään roskapostiksi.
Jäsennöinti (Vastaanotto):
- Pääpaino: Raa'an sähköpostitavutulvan purkaminen osiksi, tiettyjen otsikoiden, rungon sisällön ja liitteiden kerääminen.
- Ensisijainen työkalu:
email.parser.BytesParser
taiemail.message_from_bytes()
, sitten tuloksena olevanEmailMessage
-objektin navigointi metodeilla, kutenis_multipart()
,iter_parts()
,get_payload()
,get_filename()
ja otsikoiden käyttäminen. - Keskeiset haasteet: Virheellisten sähköpostien käsittely, merkistökoodausten oikea tunnistaminen (erityisesti silloin kun ne ovat moniselitteisiä), puuttuvien otsikoiden käsittely ja tiedon jäsennöinti monista MIME-rakenteista.
`EmailMessage`-luokalla rakentamasi viesti tulisi voida jäsentää täydellisesti `BytesParser`-luokalla. Samoin jäsennöinnin aikana tuotetun MIME-rakenteen ymmärtäminen antaa sinulle käsityksen siitä, miten voit itse rakentaa monimutkaisia viestejä.
Parhaat käytännöt globaaliin sähköpostien käsittelyyn Pythonilla
Sovelluksissa, jotka ovat vuorovaikutuksessa globaalin yleisön kanssa tai käsittelevät erilaisia sähköpostilähteitä, harkitse näitä parhaita käytäntöjä:
- Standardoi UTF-8: Käytä aina UTF-8-merkistöä kaikelle tekstisisällölle, sekä rakentaessa että odottaessa sitä jäsennöinnin aikana. Tämä on yleismaailmallinen standardi merkistökoodaukselle ja välttää mojibaken (epäselvän tekstin).
- Vahvista sähköpostiosoitteet: Vahvista vastaanottajien sähköpostiosoitteet ennen lähettämistä varmistaaksesi toimitettavuuden. Jäsennöinnin aikana varaudu mahdollisesti virheellisiin tai epämuotoiltuihin osoitteisiin `From`, `To` tai `Cc`-otsikoissa.
- Testaa perusteellisesti: Testaa sähköpostien rakentamista eri sähköpostiohjelmilla (Gmail, Outlook, Apple Mail, Thunderbird) ja alustoilla varmistaaksesi HTML:n ja liitteiden johdonmukaisen renderöinnin. Jäsennöinnin osalta testaa laajalla valikoimalla esimerkkisähköposteja, mukaan lukien ne, joissa on epätavallisia koodauksia, puuttuvia otsikoita tai monimutkaisia sisäkkäisiä rakenteita.
- Sanitoi jäsennetyt syötteet: Käsittele aina saapuvista sähköposteista kerättyä sisältöä epäluotettavana. Sanitoi HTML-sisältö estääksesi XSS-hyökkäykset, jos näytät sen verkkosovelluksessa. Vahvista liitetiedostojen nimet ja tyypit estääksesi polun läpikäynnin tai muut turvallisuusaukkoja tiedostoja tallennettaessa.
- Vankka virheiden käsittely: Ota käyttöön kattavat
try-except
-lohkot dekoodattaessa sisällön dataa tai käytettäessä mahdollisesti puuttuvia otsikoita. Käsittele gracefullyUnicodeDecodeError
taiKeyError
. - Käsittele suuria liitteitä: Huomioi liitteiden koot sekä rakentaessa (välttääksesi postipalvelimien rajat) että jäsennettäessä (välttääksesi liiallista muistin tai levytilan käyttöä). Harkitse suurten liitteiden suoratoistoa, jos järjestelmäsi tukee sitä.
- Hyödynnä
email.policy
: Kriittisissä sovelluksissa valitse nimenomaisesti `email.policy` (esim. `policy.SMTP`) varmistaaksesi tiukan sähköpostistandardien noudattamisen, mikä voi vaikuttaa toimitettavuuteen ja yhteentoimivuuteen. - Metadatan säilyttäminen: Jäsennöitäessä päätä, mitkä metatiedot (otsikot, alkuperäiset raja-arvot) ovat tärkeitä säilyttää, erityisesti jos rakennat postin arkistointi- tai välitysjärjestelmää.
Yhteenveto
Pythonin email
-paketti on uskomattoman tehokas ja joustava kirjasto kaikille, jotka tarvitsevat ohjelmallista vuorovaikutusta sähköpostin kanssa. Hallitsemalla sekä MIME-viestien rakentamisen että saapuvien sähköpostien vankan jäsennöinnin voit luoda hienostuneita sähköpostiautomaatiojärjestelmiä, rakentaa sähköpostiohjelmia, analysoida sähköpostidataa ja integroida sähköpostitoimintoja käytännössä mihin tahansa sovellukseen.
Paketti käsittelee harkiten MIME:n taustalla olevia monimutkaisuuksia, antaen kehittäjille mahdollisuuden keskittyä sähköpostivuorovaikutuksen sisältöön ja logiikkaan. Lähetätpä sitten henkilökohtaisia uutiskirjeitä globaalille yleisölle tai keräät kriittistä tietoa automaattisista järjestelmäraporteista, syvä ymmärrys email
-paketista osoittautuu korvaamattomaksi luotaessa luotettavia, yhteentoimivia ja globaalisti tietoisia sähköpostiratkaisuja.